package org.openea.eap.module.obpm.service.form.impl;

import org.openbpm.base.api.query.FieldRelation;
import org.openbpm.base.api.query.QueryFilter;
import org.openbpm.base.api.query.QueryOP;
import org.openbpm.base.api.query.WhereClause;
import org.openbpm.base.core.util.AppUtil;
import org.openbpm.base.core.util.BeanUtils;
import org.openbpm.base.core.util.StringUtil;
import org.openbpm.base.core.util.ThreadMapUtil;
import org.openbpm.base.dao.CommonDao;
import org.openbpm.base.db.dboper.DbOperator;
import org.openbpm.base.db.dboper.DbOperatorFactory;
import org.openbpm.base.db.model.query.DefaultQueryField;
import org.openbpm.base.db.model.query.DefaultQueryFilter;
import org.openbpm.base.db.model.table.Column;
import org.openbpm.base.db.model.table.Table;
import org.openbpm.base.manager.impl.BaseManager;
import org.openbpm.form.api.constant.FormCustDialogConditionFieldValueSource;
import org.openbpm.form.api.constant.FormCustDialogObjType;
import org.openbpm.form.api.constant.FormCustDialogStyle;
import org.openea.eap.module.obpm.dao.form.FormCustDialogDao;
import org.openea.eap.module.obpm.model.form.FormCustDialog;
import org.openea.eap.module.obpm.model.form.custdialog.FormCustDialogConditionField;
import org.openea.eap.module.obpm.model.form.custdialog.FormCustDialogDisplayField;
import org.openea.eap.module.obpm.model.form.custdialog.FormCustDialogReturnField;
import org.openea.eap.module.obpm.model.form.custdialog.FormCustDialogSortField;
import org.openbpm.sys.api.groovy.IGroovyScriptEngine;
import org.openbpm.sys.api.model.ISysDataSource;
import org.openbpm.sys.api.service.ISysDataSourceService;
import org.openea.eap.module.obpm.service.form.FormCustDialogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;


@Service("formCustDialogManager")
public class FormCustDialogManagerImpl
        extends BaseManager<String, FormCustDialog>
        implements FormCustDialogManager {
    @Resource
    FormCustDialogDao formCustDialogDao;
    @Autowired
    ISysDataSourceService sysDataSourceService;
    @Autowired
    CommonDao<?> commonDao;
    @Autowired
    IGroovyScriptEngine groovyScriptEngine;

    public FormCustDialog getByKey(String key) {
        DefaultQueryFilter defaultQueryFilter = new DefaultQueryFilter();
        defaultQueryFilter.addFilter("key_", key, QueryOP.EQUAL);
        return (FormCustDialog) queryOne((QueryFilter) defaultQueryFilter);
    }


    public Map<String, String> searchObjName(FormCustDialog formCustDialog) {
        ISysDataSource sysDataSource = this.sysDataSourceService.getByKey(formCustDialog.getDsKey());
        JdbcTemplate jdbcTemplate = this.sysDataSourceService.getJdbcTemplateByKey(formCustDialog.getDsKey());
        Map<String, String> map = new HashMap<>();
        DbOperator dbOperator = DbOperatorFactory.newOperator(sysDataSource.getDbType(), jdbcTemplate);

        if (FormCustDialogObjType.TABLE.equalsWithKey(formCustDialog.getObjType())) {
            map = dbOperator.getTableNames(formCustDialog.getObjName());
        } else if (FormCustDialogObjType.VIEW.equalsWithKey(formCustDialog.getObjType())) {
            List<String> viewNames = dbOperator.getViewNames(formCustDialog.getObjName());
            for (String viewName : viewNames) {
                map.put(viewName, viewName);
            }
        }
        return map;
    }


    public Table<Column> getTable(FormCustDialog formCustDialog) {
        try {
            ISysDataSource sysDataSource = this.sysDataSourceService.getByKey(formCustDialog.getDsKey());
            JdbcTemplate jdbcTemplate = this.sysDataSourceService.getJdbcTemplateByKey(formCustDialog.getDsKey());
            DbOperator dbOperator = DbOperatorFactory.newOperator(sysDataSource.getDbType(), jdbcTemplate);
            Table<Column> table = null;

            if (FormCustDialogObjType.TABLE.equalsWithKey(formCustDialog.getObjType())) {
                table = dbOperator.getTable(formCustDialog.getObjName());
            } else if (FormCustDialogObjType.VIEW.equalsWithKey(formCustDialog.getObjType())) {
                table = dbOperator.getView(formCustDialog.getObjName());
            }
            return table;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<?> data(FormCustDialog formCustDialog, QueryFilter queryFilter) {
        List<?> list;
        String sql = analyseSql(formCustDialog);
        handleQueryFilter(formCustDialog, queryFilter);

        if ("interface".equals(formCustDialog.getDataSource())) {
            return getDataByInterface(formCustDialog, queryFilter);
        }


        boolean isPage = ((Boolean) ThreadMapUtil.getOrDefault("isPage", Boolean.valueOf(true))).booleanValue();
        if (isPage) {
            list = this.commonDao.queryForListPage(sql, queryFilter);
        } else {
            queryFilter.setPage(null);
            list = this.commonDao.queryForListPage(sql, queryFilter);
        }
        return list;
    }

    private List getDataByInterface(FormCustDialog customDialog, QueryFilter queryFilter) {
        String beanMethod = customDialog.getObjName();
        if (StringUtil.isEmpty(beanMethod)) throw new RuntimeException("自定义对话框数据服务接口不能为空！");

        String[] aryHandler = beanMethod.split("[.]");
        if (aryHandler == null || aryHandler.length != 2) throw new RuntimeException("自定义对话框数据服务接口格式不正确！" + beanMethod);

        String beanId = aryHandler[0];
        String method = aryHandler[1];

        Object serviceBean = AppUtil.getBean(beanId);
        if (serviceBean == null) return null;
        try {
            Method invokeMethod = serviceBean.getClass().getDeclaredMethod(method, new Class[]{QueryFilter.class});
            return (List) invokeMethod.invoke(serviceBean, new Object[]{queryFilter});
        } catch (Exception e) {
            throw new RuntimeException("查询异常！" + e.getMessage(), e);
        }
    }


    private String analyseSql(FormCustDialog formCustDialog) {
        Set<String> columnNameSet = new HashSet<>();
        if (FormCustDialogStyle.LIST.equalsWithKey(formCustDialog.getStyle())) {
            for (FormCustDialogDisplayField field : formCustDialog.getDisplayFields()) {
                columnNameSet.add(field.getColumnName());
            }
        }
        if (FormCustDialogStyle.TREE.equalsWithKey(formCustDialog.getStyle())) {
            columnNameSet.add(formCustDialog.getTreeConfig().getPid());
            columnNameSet.add(formCustDialog.getTreeConfig().getId());
            columnNameSet.add(formCustDialog.getTreeConfig().getShowColumn());
        }

        for (FormCustDialogReturnField field : formCustDialog.getReturnFields()) {
            columnNameSet.add(field.getColumnName());
        }
        for (FormCustDialogSortField field : formCustDialog.getSortFields()) {
            columnNameSet.add(field.getColumnName());
        }


        StringBuilder displaySql = new StringBuilder();
        for (String columnName : columnNameSet) {
            if (displaySql.length() > 0) {
                displaySql.append(",");
            }
            displaySql.append(columnName);
        }

        return "select " + displaySql.toString() + " from " + formCustDialog.getObjName();
    }


    private QueryFilter handleQueryFilter(FormCustDialog formCustDialog, QueryFilter queryFilter) {

        for (FormCustDialogConditionField field : formCustDialog.getConditionFields()) {
            FieldRelation fieldRelation = null;
            if ("OR".equalsIgnoreCase(field.getFieldRelation())) {
                fieldRelation = FieldRelation.OR;
            }else if("AND".equalsIgnoreCase(field.getFieldRelation())){
                fieldRelation = FieldRelation.AND;
            }
            // 固定值
            if (FormCustDialogConditionFieldValueSource.FIXED_VALUE.equalsWithKey(field.getValueSource())) {
                Object value = BeanUtils.getValue(field.getDbType(), QueryOP.getByVal(field.getCondition()), field.getValue().getText());
                queryFilter.addFilter(field.getColumnName(), value, QueryOP.getByVal(field.getCondition()), fieldRelation);
            // 脚本
            }else if (FormCustDialogConditionFieldValueSource.SCRIPT.equalsWithKey(field.getValueSource())) {
                Object value = this.groovyScriptEngine.executeObject(field.getValue().getText(), queryFilter.getParams());
                queryFilter.addFilter(field.getColumnName(), value, QueryOP.getByVal(field.getCondition()), fieldRelation);
            }
            // 参数
            if(FormCustDialogConditionFieldValueSource.PARAM.equalsWithKey(field.getValueSource())){
                for(WhereClause whereClause:queryFilter.getFieldLogic().getWhereClauses()){
                    if(whereClause instanceof DefaultQueryField){
                        DefaultQueryField queryField =  (DefaultQueryField)whereClause;
                       if(field.getColumnName().equalsIgnoreCase(queryField.getField())){
                           queryField.setFieldRelation(fieldRelation);
                       }
                    }
                }
            }
        }

        for (FormCustDialogSortField field : formCustDialog.getSortFields()) {
            queryFilter.addFieldSort(field.getColumnName(), field.getSortType());
        }

        return queryFilter;
    }


    public boolean existsByKey(String key) {
        return (this.formCustDialogDao.existsByKey(key).intValue() > 0);
    }
}
